package com.nulldev.util.crypto.hash;

import com.nulldev.util.crypto.IMessageDigest;

public abstract class BaseHash implements IMessageDigest {
	   protected String name;
	    protected int hashSize;
	    protected int blockSize;
	    protected long count;
	    protected byte[] buffer;
	    
	    public String name() {
	        return this.name;
	    }
	    
	    public int hashSize() {
	        return this.hashSize;
	    }
	    
	    public int blockSize() {
	        return this.blockSize;
	    }
	    
	    public void update(final byte b) {
	        final int i = (int)(this.count % this.blockSize);
	        ++this.count;
	        this.buffer[i] = b;
	        if (i == this.blockSize - 1) {
	            this.transform(this.buffer, 0);
	        }
	    }
	    
	    public void update(final byte[] b, final int offset, final int len) {
	        int n = (int)(this.count % this.blockSize);
	        this.count += len;
	        final int partLen = this.blockSize - n;
	        int i = 0;
	        if (len >= partLen) {
	            System.arraycopy(b, offset, this.buffer, n, partLen);
	            this.transform(this.buffer, 0);
	            for (i = partLen; i + this.blockSize - 1 < len; i += this.blockSize) {
	                this.transform(b, offset + i);
	            }
	            n = 0;
	        }
	        if (i < len) {
	            System.arraycopy(b, offset + i, this.buffer, n, len - i);
	        }
	    }
	    
	    public byte[] digest() {
	        final byte[] tail = this.padBuffer();
	        this.update(tail, 0, tail.length);
	        final byte[] result = this.getResult();
	        this.reset();
	        return result;
	    }
	    
	    public void reset() {
	        this.count = 0L;
	        for (int i = 0; i < this.blockSize; this.buffer[i++] = 0) {}
	        this.resetContext();
	    }
	    
	    public abstract Object clone();
	    
	    public abstract boolean selfTest();
	    
	    protected abstract byte[] padBuffer();
	    
	    protected abstract byte[] getResult();
	    
	    protected abstract void resetContext();
	    
	    protected abstract void transform(final byte[] p0, final int p1);
	    
	    protected BaseHash(final String name, final int hashSize, final int blockSize) {
	        this.name = name;
	        this.hashSize = hashSize;
	        this.blockSize = blockSize;
	        this.buffer = new byte[blockSize];
	        this.resetContext();
	    }
}
